{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JMio44BDpYtw"
      },
      "source": [
        "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
        "\n",
        "```shell\n",
        "pip install tensorflow-cpu\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T07:49:33.225136Z",
          "start_time": "2024-07-22T07:49:29.446561200Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "execution": {
          "iopub.execute_input": "2025-01-21T01:54:18.213753Z",
          "iopub.status.busy": "2025-01-21T01:54:18.213456Z",
          "iopub.status.idle": "2025-01-21T01:54:23.260279Z",
          "shell.execute_reply": "2025-01-21T01:54:23.259771Z",
          "shell.execute_reply.started": "2025-01-21T01:54:18.213734Z"
        },
        "id": "YMWskinNpYtz",
        "outputId": "d8b9057d-e01e-4868-84a6-be7f09e1e97b",
        "tags": []
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "sys.version_info(major=3, minor=11, micro=11, releaselevel='final', serial=0)\n",
            "matplotlib 3.10.0\n",
            "numpy 1.26.4\n",
            "pandas 2.2.2\n",
            "sklearn 1.6.0\n",
            "torch 2.5.1+cu121\n",
            "cuda:0\n"
          ]
        }
      ],
      "source": [
        "import matplotlib as mpl\n",
        "import matplotlib.pyplot as plt\n",
        "%matplotlib inline\n",
        "import numpy as np\n",
        "import sklearn\n",
        "import pandas as pd\n",
        "import os\n",
        "import sys\n",
        "import time\n",
        "from tqdm.auto import tqdm\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.nn.functional as F\n",
        "\n",
        "print(sys.version_info)\n",
        "for module in mpl, np, pd, sklearn, torch:\n",
        "    print(module.__name__, module.__version__)\n",
        "\n",
        "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
        "print(device)\n",
        "\n",
        "seed = 42\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "execution": {
          "iopub.execute_input": "2025-01-21T01:54:31.913228Z",
          "iopub.status.busy": "2025-01-21T01:54:31.912829Z",
          "iopub.status.idle": "2025-01-21T01:54:35.508261Z",
          "shell.execute_reply": "2025-01-21T01:54:35.507727Z",
          "shell.execute_reply.started": "2025-01-21T01:54:31.913207Z"
        },
        "id": "olzoueSyqofw",
        "outputId": "a7bd5160-3072-4c71-db5c-3b57a7e0c474",
        "tags": []
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Requirement already satisfied: kaggle in /usr/local/lib/python3.11/dist-packages (1.6.17)\n",
            "Requirement already satisfied: six>=1.10 in /usr/local/lib/python3.11/dist-packages (from kaggle) (1.17.0)\n",
            "Requirement already satisfied: certifi>=2023.7.22 in /usr/local/lib/python3.11/dist-packages (from kaggle) (2024.12.14)\n",
            "Requirement already satisfied: python-dateutil in /usr/local/lib/python3.11/dist-packages (from kaggle) (2.8.2)\n",
            "Requirement already satisfied: requests in /usr/local/lib/python3.11/dist-packages (from kaggle) (2.32.3)\n",
            "Requirement already satisfied: tqdm in /usr/local/lib/python3.11/dist-packages (from kaggle) (4.67.1)\n",
            "Requirement already satisfied: python-slugify in /usr/local/lib/python3.11/dist-packages (from kaggle) (8.0.4)\n",
            "Requirement already satisfied: urllib3 in /usr/local/lib/python3.11/dist-packages (from kaggle) (2.3.0)\n",
            "Requirement already satisfied: bleach in /usr/local/lib/python3.11/dist-packages (from kaggle) (6.2.0)\n",
            "Requirement already satisfied: webencodings in /usr/local/lib/python3.11/dist-packages (from bleach->kaggle) (0.5.1)\n",
            "Requirement already satisfied: text-unidecode>=1.3 in /usr/local/lib/python3.11/dist-packages (from python-slugify->kaggle) (1.3)\n",
            "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests->kaggle) (3.4.1)\n",
            "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests->kaggle) (3.10)\n"
          ]
        }
      ],
      "source": [
        "!pip install kaggle"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "ExecutionIndicator": {
          "show": true
        },
        "execution": {
          "iopub.execute_input": "2025-01-21T01:54:48.174953Z",
          "iopub.status.busy": "2025-01-21T01:54:48.174593Z",
          "iopub.status.idle": "2025-01-21T01:54:48.287922Z",
          "shell.execute_reply": "2025-01-21T01:54:48.287373Z",
          "shell.execute_reply.started": "2025-01-21T01:54:48.174930Z"
        },
        "tags": [],
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "FrsOa9-dHRxW",
        "outputId": "105ae472-b4b9-4a14-c522-eda79bf102b9"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "/content\n"
          ]
        }
      ],
      "source": [
        "!pwd"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "ExecutionIndicator": {
          "show": false
        },
        "execution": {
          "iopub.execute_input": "2025-01-21T02:01:14.100470Z",
          "iopub.status.busy": "2025-01-21T02:01:14.100150Z",
          "iopub.status.idle": "2025-01-21T02:01:14.214430Z",
          "shell.execute_reply": "2025-01-21T02:01:14.213878Z",
          "shell.execute_reply.started": "2025-01-21T02:01:14.100449Z"
        },
        "tags": [],
        "id": "pXGb5SfTHRxW",
        "outputId": "c81c7b77-a326-4839-b618-2780bb61e84f"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "slothkong\n"
          ]
        }
      ],
      "source": [
        "!rm -r /content/datasets/"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-21T01:58:54.677371Z",
          "iopub.status.busy": "2025-01-21T01:58:54.677059Z",
          "iopub.status.idle": "2025-01-21T01:58:54.680747Z",
          "shell.execute_reply": "2025-01-21T01:58:54.680260Z",
          "shell.execute_reply.started": "2025-01-21T01:58:54.677349Z"
        },
        "id": "L8O3V3yJqbWH",
        "tags": []
      },
      "outputs": [],
      "source": [
        "import json\n",
        "token = {\"username\":\"heartheart\",\"key\":\"19f40dbd10ba3a3baeb37c061408d48f\"}\n",
        "with open('/content/kaggle.json', 'w') as file:\n",
        "  json.dump(token, file)#json.dump类似于write"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-21T01:59:44.683833Z",
          "iopub.status.busy": "2025-01-21T01:59:44.683518Z",
          "iopub.status.idle": "2025-01-21T01:59:44.798520Z",
          "shell.execute_reply": "2025-01-21T01:59:44.797966Z",
          "shell.execute_reply.started": "2025-01-21T01:59:44.683814Z"
        },
        "tags": [],
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "P6e_XVIDHRxX",
        "outputId": "69f2bb95-e82b-44cd-ddad-b5cf1860d216"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "{\"username\": \"heartheart\", \"key\": \"19f40dbd10ba3a3baeb37c061408d48f\"}"
          ]
        }
      ],
      "source": [
        "!cat /content/kaggle.json"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "execution": {
          "iopub.execute_input": "2025-01-21T02:00:58.382650Z",
          "iopub.status.busy": "2025-01-21T02:00:58.382286Z",
          "iopub.status.idle": "2025-01-21T02:00:59.110089Z",
          "shell.execute_reply": "2025-01-21T02:00:59.109480Z",
          "shell.execute_reply.started": "2025-01-21T02:00:58.382628Z"
        },
        "id": "Pf8ZxoKzqgzu",
        "outputId": "94ccfe48-cecb-44dc-b008-3a1c81ef7c29",
        "tags": []
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "- path is now set to: /content\n"
          ]
        }
      ],
      "source": [
        "!mkdir -p ~/.kaggle\n",
        "!cp /content/kaggle.json ~/.kaggle/\n",
        "!chmod 600 ~/.kaggle/kaggle.json\n",
        "!kaggle config set -n path -v /content"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T11:25:18.594994Z",
          "iopub.status.busy": "2025-01-20T11:25:18.594751Z",
          "iopub.status.idle": "2025-01-20T11:28:58.355053Z",
          "shell.execute_reply": "2025-01-20T11:28:58.354552Z",
          "shell.execute_reply.started": "2025-01-20T11:25:18.594971Z"
        },
        "id": "B8hQi6ejqkAG",
        "outputId": "a702e641-20b9-4073-dca6-36454be14ee0"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Dataset URL: https://www.kaggle.com/datasets/slothkong/10-monkey-species\n",
            "License(s): CC0-1.0\n",
            "Downloading 10-monkey-species.zip to /content/datasets/slothkong/10-monkey-species\n",
            "100% 546M/547M [00:24<00:00, 24.6MB/s]\n",
            "100% 547M/547M [00:24<00:00, 23.1MB/s]\n"
          ]
        }
      ],
      "source": [
        "!kaggle datasets download -d slothkong/10-monkey-species"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "ExecutionIndicator": {
          "show": true
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T11:59:51.417129Z",
          "iopub.status.busy": "2025-01-20T11:59:51.416835Z",
          "iopub.status.idle": "2025-01-20T11:59:54.927997Z",
          "shell.execute_reply": "2025-01-20T11:59:54.927331Z",
          "shell.execute_reply.started": "2025-01-20T11:59:51.417108Z"
        },
        "id": "tporD6JYsFQ-",
        "tags": []
      },
      "outputs": [],
      "source": [
        "!unzip -o -d /content /content/datasets/slothkong/10-monkey-species/10-monkey-species.zip >/dev/null"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-20T11:58:44.300193Z",
          "iopub.status.busy": "2025-01-20T11:58:44.299847Z",
          "iopub.status.idle": "2025-01-20T11:58:44.416421Z",
          "shell.execute_reply": "2025-01-20T11:58:44.415926Z",
          "shell.execute_reply.started": "2025-01-20T11:58:44.300174Z"
        },
        "tags": [],
        "id": "cy48ExDcHRxZ",
        "outputId": "a31b1b78-c8b4-4985-a545-fa081a1355e0"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "04_10_monkeys_model_1_colab.ipynb  kaggle.json\n"
          ]
        }
      ],
      "source": [
        "!ls"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:52:17.543980900Z",
          "start_time": "2024-07-22T08:52:17.520817400Z"
        },
        "ExecutionIndicator": {
          "show": true
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:00:15.247603Z",
          "iopub.status.busy": "2025-01-20T12:00:15.247268Z",
          "iopub.status.idle": "2025-01-20T12:00:15.258693Z",
          "shell.execute_reply": "2025-01-20T12:00:15.258285Z",
          "shell.execute_reply.started": "2025-01-20T12:00:15.247581Z"
        },
        "id": "nnayCtXTpYt2",
        "outputId": "d256d121-e378-4e64-a75f-44c8874f0d6d",
        "tags": []
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "load 1097 images from training dataset\n",
            "load 272 images from validation dataset\n"
          ]
        }
      ],
      "source": [
        "from torchvision import datasets\n",
        "from torchvision.transforms import ToTensor, Resize, Compose, ConvertImageDtype, Normalize\n",
        "\n",
        "\n",
        "from pathlib import Path\n",
        "\n",
        "DATA_DIR1 = Path(\"/content/training/\")\n",
        "DATA_DIR2 = Path(\"/content/validation/\")\n",
        "\n",
        "class MonkeyDataset(datasets.ImageFolder):\n",
        "    def __init__(self, mode, transform=None):\n",
        "        if mode == \"train\":\n",
        "            root = DATA_DIR1 / \"training\"\n",
        "        elif mode == \"val\":\n",
        "            root = DATA_DIR2 / \"validation\"\n",
        "        else:\n",
        "            raise ValueError(\"mode should be one of the following: train, val, but got {}\".format(mode))\n",
        "        super().__init__(root, transform) # 调用父类init方法\n",
        "        self.imgs = self.samples # self.samples里边是图片路径及标签 [(path, label), (path, label),...]\n",
        "        self.targets = [s[1] for s in self.samples] # 标签取出来\n",
        "\n",
        "# 预先设定的图片尺寸\n",
        "img_h, img_w = 128, 128\n",
        "transform = Compose([\n",
        "    Resize((img_h, img_w)), # 图片缩放\n",
        "    ToTensor(),\n",
        "    # 预先统计的\n",
        "    Normalize([0.4363, 0.4328, 0.3291], [0.2085, 0.2032, 0.1988]),\n",
        "    ConvertImageDtype(torch.float), # 转换为float类型\n",
        "]) #数据预处理\n",
        "\n",
        "\n",
        "train_ds = MonkeyDataset(\"train\", transform=transform)\n",
        "val_ds = MonkeyDataset(\"val\", transform=transform)\n",
        "\n",
        "print(\"load {} images from training dataset\".format(len(train_ds)))\n",
        "print(\"load {} images from validation dataset\".format(len(val_ds)))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:15:15.286096100Z",
          "start_time": "2024-07-22T08:15:15.272778500Z"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:00:30.179526Z",
          "iopub.status.busy": "2025-01-20T12:00:30.179216Z",
          "iopub.status.idle": "2025-01-20T12:00:30.183856Z",
          "shell.execute_reply": "2025-01-20T12:00:30.183353Z",
          "shell.execute_reply.started": "2025-01-20T12:00:30.179507Z"
        },
        "id": "oS2jYFvMpYt3",
        "outputId": "daef9180-7955-4875-e980-e986fb0defd7",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['n0', 'n1', 'n2', 'n3', 'n4', 'n5', 'n6', 'n7', 'n8', 'n9']"
            ]
          },
          "metadata": {},
          "execution_count": 11
        }
      ],
      "source": [
        "# 数据类别\n",
        "train_ds.classes"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:15:24.968412700Z",
          "start_time": "2024-07-22T08:15:24.960415300Z"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:00:30.184936Z",
          "iopub.status.busy": "2025-01-20T12:00:30.184565Z",
          "iopub.status.idle": "2025-01-20T12:00:30.188057Z",
          "shell.execute_reply": "2025-01-20T12:00:30.187590Z",
          "shell.execute_reply.started": "2025-01-20T12:00:30.184919Z"
        },
        "id": "8SJAYnQopYt4",
        "outputId": "f9254274-8b44-44e2-993a-cb96f2836fa5",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'n0': 0,\n",
              " 'n1': 1,\n",
              " 'n2': 2,\n",
              " 'n3': 3,\n",
              " 'n4': 4,\n",
              " 'n5': 5,\n",
              " 'n6': 6,\n",
              " 'n7': 7,\n",
              " 'n8': 8,\n",
              " 'n9': 9}"
            ]
          },
          "metadata": {},
          "execution_count": 12
        }
      ],
      "source": [
        "train_ds.class_to_idx"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:22:41.089921600Z",
          "start_time": "2024-07-22T08:22:41.076096400Z"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:00:30.188668Z",
          "iopub.status.busy": "2025-01-20T12:00:30.188512Z",
          "iopub.status.idle": "2025-01-20T12:00:30.248807Z",
          "shell.execute_reply": "2025-01-20T12:00:30.248336Z",
          "shell.execute_reply.started": "2025-01-20T12:00:30.188654Z"
        },
        "id": "o2x2L3yTpYt4",
        "outputId": "d6bf6650-187c-48aa-fcec-27c0ef25506f",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "/content/training/training/n0/n0018.jpg 0\n",
            "tensor([[[-0.1553, -0.0236,  0.3525,  ...,  1.1425,  1.8384,  0.9920],\n",
            "         [-0.1365, -0.1365,  0.1833,  ...,  0.8416,  1.9324,  0.9356],\n",
            "         [-0.1177, -0.1741,  0.1268,  ...,  1.1425,  1.3306,  1.3494],\n",
            "         ...,\n",
            "         [ 2.1205,  2.1205,  2.0641,  ...,  0.9920,  0.8416,  0.3713],\n",
            "         [ 2.0641,  1.9136,  1.6691,  ...,  0.3713, -0.0612, -0.0048],\n",
            "         [ 1.9513,  1.6691,  1.2553,  ...,  0.0328, -0.4938, -0.1365]],\n",
            "\n",
            "        [[-0.2193, -0.0456,  0.3983,  ...,  1.3632,  2.0580,  1.0737],\n",
            "         [-0.2386, -0.2000,  0.2053,  ...,  0.9386,  2.1545,  1.1702],\n",
            "         [-0.2772, -0.2772,  0.0702,  ...,  1.1702,  1.5176,  1.6527],\n",
            "         ...,\n",
            "         [ 2.4054,  2.3668,  2.2703,  ...,  0.6491,  0.5333,  0.1088],\n",
            "         [ 2.2896,  2.1352,  1.8650,  ...,  0.0509, -0.3351, -0.3544],\n",
            "         [ 2.1352,  1.8457,  1.4211,  ..., -0.3544, -0.8369, -0.5667]],\n",
            "\n",
            "        [[-0.7283, -0.4324,  0.2975,  ...,  0.2383,  0.9287, -0.3141],\n",
            "         [-0.7678, -0.6297,  0.0805,  ...,  0.1002,  1.1851, -0.3338],\n",
            "         [-0.8467, -0.7480, -0.0971,  ...,  0.3961,  0.8103,  0.6131],\n",
            "         ...,\n",
            "         [-0.4324, -0.4916, -0.6099,  ...,  0.5934,  0.4553,  0.0213],\n",
            "         [-0.5113, -0.6099, -0.8467,  ..., -0.0379, -0.5508, -0.5508],\n",
            "         [-0.6494, -0.8467, -1.1228,  ..., -0.5705, -1.2215, -0.9650]]]) 0\n"
          ]
        }
      ],
      "source": [
        "# 图片路径 及 标签\n",
        "for fpath, label in train_ds.imgs:\n",
        "    print(fpath, label)\n",
        "    break\n",
        "\n",
        "#这个和之前的dataset完全一致\n",
        "for img, label in train_ds:\n",
        "    # c, h, w  label\n",
        "    print(img, label)\n",
        "    break"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:44:58.447431700Z",
          "start_time": "2024-07-22T08:44:29.539230100Z"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:00:30.249914Z",
          "iopub.status.busy": "2025-01-20T12:00:30.249714Z",
          "iopub.status.idle": "2025-01-20T12:00:46.038022Z",
          "shell.execute_reply": "2025-01-20T12:00:46.037369Z",
          "shell.execute_reply.started": "2025-01-20T12:00:30.249900Z"
        },
        "id": "mVb-3y68pYt5",
        "outputId": "3bedcdd5-891f-4d65-bfe2-c043bfb1d0a4",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "(tensor([0.0002]), tensor([0.9999]))\n"
          ]
        }
      ],
      "source": [
        "def cal_mean_std(ds):\n",
        "    mean = 0.\n",
        "    std = 0.\n",
        "    for img, _ in ds:\n",
        "        mean += img[0:1, :, :].mean(dim=(1, 2))\n",
        "        std += img[0:1, :, :].std(dim=(1, 2))\n",
        "    mean /= len(ds)\n",
        "    std /= len(ds)\n",
        "    return mean, std\n",
        "\n",
        "# 经过 normalize 后 均值为0，方差为1\n",
        "print(cal_mean_std(train_ds))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:32:54.269143900Z",
          "start_time": "2024-07-22T08:32:25.590376800Z"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:00:46.039218Z",
          "iopub.status.busy": "2025-01-20T12:00:46.038848Z",
          "iopub.status.idle": "2025-01-20T12:01:01.867120Z",
          "shell.execute_reply": "2025-01-20T12:01:01.866643Z",
          "shell.execute_reply.started": "2025-01-20T12:00:46.039196Z"
        },
        "id": "ARxVczNhpYt5",
        "outputId": "e189921c-05ce-489e-f4f8-393c233cdd14",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "(tensor([3.6267e-05]), tensor([0.9999]))\n"
          ]
        }
      ],
      "source": [
        "def cal_mean_std(ds):\n",
        "    mean = 0.\n",
        "    std = 0.\n",
        "    for img, _ in ds:\n",
        "        mean += img[1:2, :, :].mean(dim=(1, 2))\n",
        "        std += img[1:2, :, :].std(dim=(1, 2))\n",
        "    mean /= len(ds)\n",
        "    std /= len(ds)\n",
        "    return mean, std\n",
        "\n",
        "# 经过 normalize 后 均值为0，方差为1\n",
        "print(cal_mean_std(train_ds))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:33:54.095864500Z",
          "start_time": "2024-07-22T08:33:26.014587300Z"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:01.867906Z",
          "iopub.status.busy": "2025-01-20T12:01:01.867688Z",
          "iopub.status.idle": "2025-01-20T12:01:17.736340Z",
          "shell.execute_reply": "2025-01-20T12:01:17.735739Z",
          "shell.execute_reply.started": "2025-01-20T12:01:01.867889Z"
        },
        "id": "kdtwsOxIpYt5",
        "outputId": "0ee298d5-1763-4ef6-a53b-50875f471599",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "(tensor([-6.5391e-07]), tensor([1.0002]))\n"
          ]
        }
      ],
      "source": [
        "def cal_mean_std(ds):\n",
        "    mean = 0.\n",
        "    std = 0.\n",
        "    for img, _ in ds:\n",
        "        mean += img[2:3, :, :].mean(dim=(1, 2))\n",
        "        std += img[2:3, :, :].std(dim=(1, 2))\n",
        "    mean /= len(ds)\n",
        "    std /= len(ds)\n",
        "    return mean, std\n",
        "\n",
        "# 经过 normalize 后 均值为0，方差为1\n",
        "print(cal_mean_std(train_ds))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:39:44.080468500Z",
          "start_time": "2024-07-22T08:39:15.435622600Z"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:17.737586Z",
          "iopub.status.busy": "2025-01-20T12:01:17.737048Z",
          "iopub.status.idle": "2025-01-20T12:01:17.741140Z",
          "shell.execute_reply": "2025-01-20T12:01:17.740628Z",
          "shell.execute_reply.started": "2025-01-20T12:01:17.737555Z"
        },
        "id": "2U0E5JMTpYt6"
      },
      "outputs": [],
      "source": [
        "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
        "def cal_mean_std(ds):\n",
        "    mean = 0.\n",
        "    std = 0.\n",
        "    for img, _ in ds:\n",
        "        mean += img.mean(dim=(1, 2)) #dim=(1, 2)表示计算均值后，宽和高消除（把宽和高所有的像素加起来，再除以总数）\n",
        "        std += img.std(dim=(1, 2))\n",
        "    mean /= len(ds)\n",
        "    std /= len(ds)\n",
        "    return mean, std\n",
        "\n",
        "# 经过 normalize 后 均值为0，方差为1\n",
        "# print(cal_mean_std(train_ds))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:52:57.696309200Z",
          "start_time": "2024-07-22T08:52:57.686315700Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:17.741825Z",
          "iopub.status.busy": "2025-01-20T12:01:17.741640Z",
          "iopub.status.idle": "2025-01-20T12:01:17.744950Z",
          "shell.execute_reply": "2025-01-20T12:01:17.744516Z",
          "shell.execute_reply.started": "2025-01-20T12:01:17.741809Z"
        },
        "id": "vzPl-CPOpYt6",
        "outputId": "d5c342ab-193c-4ea9-a421-231069ae58c6"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n"
          ]
        }
      ],
      "source": [
        "import torch.nn as nn\n",
        "from torch.utils.data.dataloader import DataLoader\n",
        "\n",
        "batch_size = 64\n",
        "# 从数据集到dataloader\n",
        "train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)\n",
        "val_loader = DataLoader(val_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-22T08:53:04.933884100Z",
          "start_time": "2024-07-22T08:52:59.755852600Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:17.745498Z",
          "iopub.status.busy": "2025-01-20T12:01:17.745348Z",
          "iopub.status.idle": "2025-01-20T12:01:19.540894Z",
          "shell.execute_reply": "2025-01-20T12:01:19.540289Z",
          "shell.execute_reply.started": "2025-01-20T12:01:17.745484Z"
        },
        "id": "PvzJPmCKpYt7",
        "outputId": "c0f6f3d8-f24a-4cb2-c943-746294ce9e3a"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "torch.Size([64, 3, 128, 128])\n",
            "torch.Size([64])\n"
          ]
        }
      ],
      "source": [
        "for imgs, labels in train_loader:\n",
        "    print(imgs.shape)\n",
        "    print(labels.shape)\n",
        "    break"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MxR9Zm8FpYt7"
      },
      "source": [
        "## 定义模型"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:19.542781Z",
          "iopub.status.busy": "2025-01-20T12:01:19.542514Z",
          "iopub.status.idle": "2025-01-20T12:01:19.597234Z",
          "shell.execute_reply": "2025-01-20T12:01:19.596833Z",
          "shell.execute_reply.started": "2025-01-20T12:01:19.542762Z"
        },
        "id": "93Z27pjxpYt7",
        "outputId": "6faee490-1c38-48db-931b-7c30e6c5539e",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "conv1.weight\tparamerters num: 864\n",
            "conv1.bias\tparamerters num: 32\n",
            "conv2.weight\tparamerters num: 9216\n",
            "conv2.bias\tparamerters num: 32\n",
            "conv3.weight\tparamerters num: 18432\n",
            "conv3.bias\tparamerters num: 64\n",
            "conv4.weight\tparamerters num: 36864\n",
            "conv4.bias\tparamerters num: 64\n",
            "conv5.weight\tparamerters num: 73728\n",
            "conv5.bias\tparamerters num: 128\n",
            "conv6.weight\tparamerters num: 147456\n",
            "conv6.bias\tparamerters num: 128\n",
            "fc1.weight\tparamerters num: 4194304\n",
            "fc1.bias\tparamerters num: 128\n",
            "fc2.weight\tparamerters num: 1280\n",
            "fc2.bias\tparamerters num: 10\n"
          ]
        }
      ],
      "source": [
        "\n",
        "class CNN(nn.Module):\n",
        "    def __init__(self, num_classes=10, activation=\"relu\"):\n",
        "        super(CNN, self).__init__()\n",
        "        self.activation = F.relu if activation == \"relu\" else F.selu\n",
        "        self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=\"same\")\n",
        "        self.conv2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\")\n",
        "        self.pool = nn.MaxPool2d(2, 2)\n",
        "        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=\"same\")\n",
        "        self.conv4 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=\"same\")\n",
        "        self.conv5 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=\"same\")\n",
        "        self.conv6 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=\"same\")\n",
        "        self.flatten = nn.Flatten()\n",
        "        # input shape is (3, 128, 128) so the flatten output shape is 128 * 16 * 16\n",
        "        self.fc1 = nn.Linear(128 * 16 * 16, 128)\n",
        "        self.fc2 = nn.Linear(128, num_classes)\n",
        "\n",
        "        self.init_weights()\n",
        "\n",
        "    def init_weights(self):\n",
        "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
        "        for m in self.modules():\n",
        "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
        "                nn.init.xavier_uniform_(m.weight)\n",
        "                nn.init.zeros_(m.bias)\n",
        "\n",
        "    def forward(self, x):\n",
        "        act = self.activation\n",
        "        x = self.pool(act(self.conv2(act(self.conv1(x)))))\n",
        "        x = self.pool(act(self.conv4(act(self.conv3(x)))))\n",
        "        x = self.pool(act(self.conv6(act(self.conv5(x)))))\n",
        "        x = self.flatten(x)\n",
        "        x = act(self.fc1(x))\n",
        "        x = self.fc2(x)\n",
        "        return x\n",
        "\n",
        "\n",
        "for idx, (key, value) in enumerate(CNN().named_parameters()):\n",
        "    print(f\"{key}\\tparamerters num: {np.prod(value.shape)}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1qNGeGLmpYt8"
      },
      "source": [
        "## 训练\n",
        "\n",
        "pytorch的训练需要自行实现，包括\n",
        "1. 定义损失函数\n",
        "2. 定义优化器\n",
        "3. 定义训练步\n",
        "4. 训练"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:19.597945Z",
          "iopub.status.busy": "2025-01-20T12:01:19.597745Z",
          "iopub.status.idle": "2025-01-20T12:01:19.853268Z",
          "shell.execute_reply": "2025-01-20T12:01:19.852817Z",
          "shell.execute_reply.started": "2025-01-20T12:01:19.597930Z"
        },
        "id": "mz4gW8hupYt8"
      },
      "outputs": [],
      "source": [
        "from sklearn.metrics import accuracy_score\n",
        "\n",
        "@torch.no_grad()\n",
        "def evaluating(model, dataloader, loss_fct):\n",
        "    loss_list = []\n",
        "    pred_list = []\n",
        "    label_list = []\n",
        "    for datas, labels in dataloader:\n",
        "        datas = datas.to(device)\n",
        "        labels = labels.to(device)\n",
        "        # 前向计算\n",
        "        logits = model(datas)\n",
        "        loss = loss_fct(logits, labels)         # 验证集损失\n",
        "        loss_list.append(loss.item())\n",
        "\n",
        "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
        "        pred_list.extend(preds.cpu().numpy().tolist())\n",
        "        label_list.extend(labels.cpu().numpy().tolist())\n",
        "\n",
        "    acc = accuracy_score(label_list, pred_list)\n",
        "    return np.mean(loss_list), acc\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vK16Ii8KpYt8"
      },
      "source": [
        "### TensorBoard 可视化\n",
        "\n",
        "\n",
        "训练过程中可以使用如下命令启动tensorboard服务。\n",
        "\n",
        "```shell\n",
        "tensorboard \\\n",
        "    --logdir=runs \\     # log 存放路径\n",
        "    --host 0.0.0.0 \\    # ip\n",
        "    --port 8848         # 端口\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:19.854526Z",
          "iopub.status.busy": "2025-01-20T12:01:19.853906Z",
          "iopub.status.idle": "2025-01-20T12:01:20.038985Z",
          "shell.execute_reply": "2025-01-20T12:01:20.038581Z",
          "shell.execute_reply.started": "2025-01-20T12:01:19.854495Z"
        },
        "id": "0RaJusY7pYt9"
      },
      "outputs": [],
      "source": [
        "from torch.utils.tensorboard import SummaryWriter\n",
        "\n",
        "\n",
        "class TensorBoardCallback:\n",
        "    def __init__(self, log_dir, flush_secs=10):\n",
        "        \"\"\"\n",
        "        Args:\n",
        "            log_dir (str): dir to write log.\n",
        "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
        "        \"\"\"\n",
        "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
        "\n",
        "    def draw_model(self, model, input_shape):\n",
        "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
        "\n",
        "    def add_loss_scalars(self, step, loss, val_loss):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/loss\",\n",
        "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
        "            global_step=step,\n",
        "            )\n",
        "\n",
        "    def add_acc_scalars(self, step, acc, val_acc):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/accuracy\",\n",
        "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
        "            global_step=step,\n",
        "        )\n",
        "\n",
        "    def add_lr_scalars(self, step, learning_rate):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/learning_rate\",\n",
        "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
        "            global_step=step,\n",
        "\n",
        "        )\n",
        "\n",
        "    def __call__(self, step, **kwargs):\n",
        "        # add loss\n",
        "        loss = kwargs.pop(\"loss\", None)\n",
        "        val_loss = kwargs.pop(\"val_loss\", None)\n",
        "        if loss is not None and val_loss is not None:\n",
        "            self.add_loss_scalars(step, loss, val_loss)\n",
        "        # add acc\n",
        "        acc = kwargs.pop(\"acc\", None)\n",
        "        val_acc = kwargs.pop(\"val_acc\", None)\n",
        "        if acc is not None and val_acc is not None:\n",
        "            self.add_acc_scalars(step, acc, val_acc)\n",
        "        # add lr\n",
        "        learning_rate = kwargs.pop(\"lr\", None)\n",
        "        if learning_rate is not None:\n",
        "            self.add_lr_scalars(step, learning_rate)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RhHZoT43pYt-"
      },
      "source": [
        "### Save Best\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:20.040145Z",
          "iopub.status.busy": "2025-01-20T12:01:20.039576Z",
          "iopub.status.idle": "2025-01-20T12:01:20.044816Z",
          "shell.execute_reply": "2025-01-20T12:01:20.044278Z",
          "shell.execute_reply.started": "2025-01-20T12:01:20.040114Z"
        },
        "id": "X6-gIb2YpYt_"
      },
      "outputs": [],
      "source": [
        "class SaveCheckpointsCallback:\n",
        "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
        "        \"\"\"\n",
        "        Save checkpoints each save_epoch epoch.\n",
        "        We save checkpoint by epoch in this implementation.\n",
        "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
        "\n",
        "        Args:\n",
        "            save_dir (str): dir to save checkpoint\n",
        "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
        "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
        "        \"\"\"\n",
        "        self.save_dir = save_dir\n",
        "        self.save_step = save_step\n",
        "        self.save_best_only = save_best_only\n",
        "        self.best_metrics = -1\n",
        "\n",
        "        # mkdir\n",
        "        if not os.path.exists(self.save_dir):\n",
        "            os.mkdir(self.save_dir)\n",
        "\n",
        "    def __call__(self, step, state_dict, metric=None):\n",
        "        if step % self.save_step > 0:\n",
        "            return\n",
        "\n",
        "        if self.save_best_only:\n",
        "            assert metric is not None\n",
        "            if metric >= self.best_metrics:\n",
        "                # save checkpoints\n",
        "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
        "                # update best metrics\n",
        "                self.best_metrics = metric\n",
        "        else:\n",
        "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BtnJ4AlcpYt_"
      },
      "source": [
        "### Early Stop"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:20.045958Z",
          "iopub.status.busy": "2025-01-20T12:01:20.045479Z",
          "iopub.status.idle": "2025-01-20T12:01:20.049866Z",
          "shell.execute_reply": "2025-01-20T12:01:20.049431Z",
          "shell.execute_reply.started": "2025-01-20T12:01:20.045929Z"
        },
        "id": "qLSAWIq8pYuA"
      },
      "outputs": [],
      "source": [
        "class EarlyStopCallback:\n",
        "    def __init__(self, patience=5, min_delta=0.01):\n",
        "        \"\"\"\n",
        "\n",
        "        Args:\n",
        "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
        "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute\n",
        "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
        "        \"\"\"\n",
        "        self.patience = patience\n",
        "        self.min_delta = min_delta\n",
        "        self.best_metric = -1\n",
        "        self.counter = 0\n",
        "\n",
        "    def __call__(self, metric):\n",
        "        if metric >= self.best_metric + self.min_delta:\n",
        "            # update best metric\n",
        "            self.best_metric = metric\n",
        "            # reset counter\n",
        "            self.counter = 0\n",
        "        else:\n",
        "            self.counter += 1\n",
        "\n",
        "    @property\n",
        "    def early_stop(self):\n",
        "        return self.counter >= self.patience\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {
        "colab": {
          "referenced_widgets": [
            "c97f64611fe340a18998e18ffdcfa81b",
            "894a6eccd0b24c46a49541086d5de8a9",
            "0fff971ee8544a61b696c553effb86e2",
            "6efe779368a844deb3b1c6b01d69e10b",
            "85e47a7d0421413fbad4394eea4ecfa3",
            "8084f217ab8b42c89374837fdcd902cd",
            "c25895a5f4604a20b0202f0af8ab85a4",
            "1cfc693b7be34683a814fe96514d2433",
            "877102ec558046e083a8d53aea385a88",
            "8cca009150a444ac934a247a6a167fdb",
            "7eb9cee1a80e460a9717a829b88d9065"
          ],
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "execution": {
          "iopub.execute_input": "2025-01-20T12:01:20.050717Z",
          "iopub.status.busy": "2025-01-20T12:01:20.050429Z",
          "iopub.status.idle": "2025-01-20T12:03:09.996413Z",
          "shell.execute_reply": "2025-01-20T12:03:09.995883Z",
          "shell.execute_reply.started": "2025-01-20T12:01:20.050702Z"
        },
        "id": "p9T_O6X7pYuA",
        "outputId": "0fa31c12-b373-44fa-a99b-8df5d4aa3d32",
        "tags": []
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "  0%|          | 0/360 [00:00<?, ?it/s]"
            ],
            "application/vnd.jupyter.widget-view+json": {
              "version_major": 2,
              "version_minor": 0,
              "model_id": "c97f64611fe340a18998e18ffdcfa81b"
            }
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Early stop at epoch 15 / global_step 270\n"
          ]
        }
      ],
      "source": [
        "# 训练\n",
        "def training(\n",
        "    model,\n",
        "    train_loader,\n",
        "    val_loader,\n",
        "    epoch,\n",
        "    loss_fct,\n",
        "    optimizer,\n",
        "    tensorboard_callback=None,\n",
        "    save_ckpt_callback=None,\n",
        "    early_stop_callback=None,\n",
        "    eval_step=500,\n",
        "    ):\n",
        "    record_dict = {\n",
        "        \"train\": [],\n",
        "        \"val\": []\n",
        "    }\n",
        "\n",
        "    global_step = 0\n",
        "    model.train()\n",
        "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
        "        for epoch_id in range(epoch):\n",
        "            # training\n",
        "            for datas, labels in train_loader:\n",
        "                datas = datas.to(device)\n",
        "                labels = labels.to(device)\n",
        "                # 梯度清空\n",
        "                optimizer.zero_grad()\n",
        "                # 模型前向计算\n",
        "                logits = model(datas)\n",
        "                # 计算损失\n",
        "                loss = loss_fct(logits, labels)\n",
        "                # 梯度回传\n",
        "                loss.backward()\n",
        "                # 调整优化器，包括学习率的变动等\n",
        "                optimizer.step()\n",
        "                preds = logits.argmax(axis=-1)\n",
        "\n",
        "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())\n",
        "                loss = loss.cpu().item()\n",
        "                # record\n",
        "\n",
        "                record_dict[\"train\"].append({\n",
        "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
        "                })\n",
        "\n",
        "                # evaluating\n",
        "                if global_step % eval_step == 0:\n",
        "                    model.eval()\n",
        "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
        "                    record_dict[\"val\"].append({\n",
        "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
        "                    })\n",
        "                    model.train()\n",
        "\n",
        "                    # 1. 使用 tensorboard 可视化\n",
        "                    if tensorboard_callback is not None:\n",
        "                        tensorboard_callback(\n",
        "                            global_step,\n",
        "                            loss=loss, val_loss=val_loss,\n",
        "                            acc=acc, val_acc=val_acc,\n",
        "                            lr=optimizer.param_groups[0][\"lr\"],\n",
        "                            )\n",
        "\n",
        "                    # 2. 保存模型权重 save model checkpoint\n",
        "                    if save_ckpt_callback is not None:\n",
        "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
        "\n",
        "                    # 3. 早停 Early Stop\n",
        "                    if early_stop_callback is not None:\n",
        "                        early_stop_callback(val_acc)\n",
        "                        if early_stop_callback.early_stop:\n",
        "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
        "                            return record_dict\n",
        "\n",
        "                # udate step\n",
        "                global_step += 1\n",
        "                pbar.update(1)\n",
        "                pbar.set_postfix({\"epoch\": epoch_id})\n",
        "\n",
        "    return record_dict\n",
        "\n",
        "\n",
        "epoch = 20\n",
        "\n",
        "activation = \"relu\"\n",
        "model = CNN(num_classes=10, activation=activation)\n",
        "\n",
        "# 1. 定义损失函数 采用交叉熵损失\n",
        "loss_fct = nn.CrossEntropyLoss()\n",
        "# 2. 定义优化器 采用 adam\n",
        "# Optimizers specified in the torch.optim package\n",
        "optimizer = torch.optim.Adam(model.parameters(), lr=0.001, eps=1e-7)\n",
        "\n",
        "# 1. tensorboard 可视化\n",
        "if not os.path.exists(\"runs\"):\n",
        "    os.mkdir(\"runs\")\n",
        "tensorboard_callback = TensorBoardCallback(f\"runs/monkeys-cnn-{activation}\")\n",
        "tensorboard_callback.draw_model(model, [1, 3, img_h, img_w])\n",
        "# 2. save best\n",
        "if not os.path.exists(\"checkpoints\"):\n",
        "    os.makedirs(\"checkpoints\")\n",
        "save_ckpt_callback = SaveCheckpointsCallback(f\"checkpoints/monkeys-cnn-{activation}\", save_step=len(train_loader), save_best_only=True)\n",
        "# 3. early stop\n",
        "early_stop_callback = EarlyStopCallback(patience=5)\n",
        "\n",
        "model = model.to(device)\n",
        "record = training(\n",
        "    model,\n",
        "    train_loader,\n",
        "    val_loader,\n",
        "    epoch,\n",
        "    loss_fct,\n",
        "    optimizer,\n",
        "    tensorboard_callback=tensorboard_callback,\n",
        "    save_ckpt_callback=save_ckpt_callback,\n",
        "    early_stop_callback=early_stop_callback,\n",
        "    eval_step=len(train_loader)\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-20T12:03:09.997301Z",
          "iopub.status.busy": "2025-01-20T12:03:09.997072Z",
          "iopub.status.idle": "2025-01-20T12:03:10.211833Z",
          "shell.execute_reply": "2025-01-20T12:03:10.211419Z",
          "shell.execute_reply.started": "2025-01-20T12:03:09.997283Z"
        },
        "id": "m_D2srP_pYuC",
        "outputId": "fcdde28d-dab8-4ce8-eb94-5192d85c0c5e",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 465
        }
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1000x500 with 2 Axes>"
            ],
            "image/png": "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KL7KykpkZWUZ4ouOjkZFRQUOHTpkKLNz507o9XrD/7CW6ty5c7h48SL8/PwAWFasQgjMnj0bmzZtws6dO9GjRw+j82357EZHR+PYsWNGDWhqaiqUSiX69OlzawJpg9Zibc5vv/0GAEbvrSXESqZh22R9bZOtt0sA2yZL+ffaJtsm866F0LHWr18vHBwcxJo1a8SJEyfE448/LlQqldFKEZboueeeE+np6SI/P1/s27dPxMfHC09PT1FSUiKEEGLmzJmie/fuYufOneLgwYMiOjpaREdHm7nWbVdVVSUOHz4sDh8+LACIFStWiMOHD4szZ84IIYR49dVXhUqlEt999504evSomDBhgujRo4eoq6szPEZiYqLo37+/yMrKEnv37hUhISFi2rRp5gqpRdeLtaqqSsydO1dkZmaK/Px8sWPHDjFgwAAREhIi6uvrDY9hKbE+8cQTokuXLiI9PV0UFRUZbrW1tYYyrX12tVqt6Nevnxg9erT47bffxLZt24SXl5eYP3++OUJqUWux5uXliSVLloiDBw+K/Px88d1334ng4GARGxtreAxLiZVMx7bJ8tomW2qXhGDbxLbJetomq050hBDi3XffFd27dxf29vZiyJAh4ueffzZ3lW7afffdJ/z8/IS9vb3o2rWruO+++0ReXp7hfF1dnXjyySeFm5ubcHJyEpMmTRJFRUVmrLFpdu3aJQA0uU2fPl0I0bCU54IFC4SPj49wcHAQcXFxIjc31+gxLl68KKZNmyZcXFyEUqkUDz/8sKiqqjJDNNd3vVhra2vF6NGjhZeXl5DL5SIwMFDMmDGjyZchS4m1uTgBiNWrVxvKtOWzW1BQIMaMGSMUCoXw9PQUzz33nNBoNLc4mutrLdbCwkIRGxsr3N3dhYODg+jVq5eYN2+euHz5stHjWEKsdGPYNllW22RL7ZIQbJvYNllP2yQRQoj27yciIiIiIiIyH6udo0NERERERLaLiQ4REREREVkdJjpERERERGR1mOgQEREREZHVYaJDRERERERWh4kOERERERFZHSY6RERERERkdZjoEBERERGR1WGiQ9ROUlJSMHHiRHNXg4iICADbJSImOkREREREZHWY6BCZaMOGDYiIiIBCoYCHhwfi4+Mxb948fPbZZ/juu+8gkUggkUiQnp4OADh79iymTp0KlUoFd3d3TJgwAQUFBYbHa/zF7eWXX4aXlxeUSiVmzpwJtVptngCJiMiisF0iap6duStAZEmKioowbdo0vP7665g0aRKqqqqwZ88ePPTQQygsLERlZSVWr14NAHB3d4dGo0FCQgKio6OxZ88e2NnZ4d///jcSExNx9OhR2NvbAwDS0tLg6OiI9PR0FBQU4OGHH4aHhwf+53/+x5zhEhFRJ8d2iahlTHSITFBUVAStVoukpCQEBgYCACIiIgAACoUCV65cga+vr6H8F198Ab1ej08++QQSiQQAsHr1aqhUKqSnp2P06NEAAHt7e3z66adwcnJC3759sWTJEsybNw9Lly6FVMqOVyIiah7bJaKW8ZNKZILIyEjExcUhIiICU6ZMwccff4xLly61WP7IkSPIy8uDq6srXFxc4OLiAnd3d9TX1+P06dNGj+vk5GS4Hx0djerqapw9e7ZD4yEiIsvGdomoZezRITKBTCZDamoq9u/fj+3bt+Pdd9/Fv/71L2RlZTVbvrq6GgMHDsTatWubnPPy8uro6hIRkZVju0TUMiY6RCaSSCSIiYlBTEwMFi5ciMDAQGzatAn29vbQ6XRGZQcMGICvvvoK3t7eUCqVLT7mkSNHUFdXB4VCAQD4+eef4eLigoCAgA6NhYiILB/bJaLmcegakQmysrLwyiuv4ODBgygsLMTGjRtRWlqK8PBwBAUF4ejRo8jNzUVZWRk0Gg2Sk5Ph6emJCRMmYM+ePcjPz0d6ejqeeuopnDt3zvC4arUajz76KE6cOIEtW7Zg0aJFmD17NsdBExHRdbFdImoZe3SITKBUKpGRkYGVK1eisrISgYGBWL58OcaMGYNBgwYhPT0dgwYNQnV1NXbt2oURI0YgIyMDzz//PJKSklBVVYWuXbsiLi7O6Je0uLg4hISEIDY2FleuXMG0adOwePFi8wVKREQWge0SUcskQghh7koQ2bKUlBRUVFRg8+bN5q4KERER2yWyGux/JCIiIiIiq8NEh4iIiIiIrA6HrhERERERkdVhjw4REREREVkdJjpERERERGR1mOgQEREREZHVYaJDRERERERWh4kOERERERFZHSY6RERERERkdZjoEBERERGR1WGiQ0REREREVoeJDhERERERWZ3/D/tF+Tb4iaNKAAAAAElFTkSuQmCC\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "#画线要注意的是损失是不一定在零到1之间的\n",
        "def plot_learning_curves(record_dict, sample_step=500):\n",
        "    # build DataFrame\n",
        "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
        "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
        "\n",
        "    # plot\n",
        "    fig_num = len(train_df.columns)\n",
        "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
        "    for idx, item in enumerate(train_df.columns):\n",
        "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
        "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
        "        axs[idx].grid()\n",
        "        axs[idx].legend()\n",
        "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
        "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
        "        axs[idx].set_xlabel(\"step\")\n",
        "\n",
        "    plt.show()\n",
        "\n",
        "plot_learning_curves(record, sample_step=10)  #横坐标是 steps"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l9N2QrfFpYuD"
      },
      "source": [
        "# 评估"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "metadata": {
        "execution": {
          "iopub.execute_input": "2025-01-20T12:03:10.212522Z",
          "iopub.status.busy": "2025-01-20T12:03:10.212313Z",
          "iopub.status.idle": "2025-01-20T12:03:11.741987Z",
          "shell.execute_reply": "2025-01-20T12:03:11.741471Z",
          "shell.execute_reply.started": "2025-01-20T12:03:10.212506Z"
        },
        "id": "qiN3FDcJpYuE",
        "outputId": "7689878a-3f11-4051-9eb7-4aca066f5a83",
        "tags": [],
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "<ipython-input-29-5cfd5a3971ab>:4: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
            "  model.load_state_dict(torch.load(f\"checkpoints/monkeys-cnn-{activation}/best.ckpt\", map_location=\"cpu\"))\n",
            "/usr/local/lib/python3.11/dist-packages/torch/utils/data/dataloader.py:617: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.\n",
            "  warnings.warn(\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "loss:     2.7598\n",
            "accuracy: 0.5588\n"
          ]
        }
      ],
      "source": [
        "# dataload for evaluating\n",
        "\n",
        "# load checkpoints\n",
        "model.load_state_dict(torch.load(f\"checkpoints/monkeys-cnn-{activation}/best.ckpt\", map_location=\"cpu\"))\n",
        "\n",
        "model.eval()\n",
        "loss, acc = evaluating(model, val_loader, loss_fct)\n",
        "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": [],
      "gpuType": "T4"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.14"
    },
    "accelerator": "GPU",
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "c97f64611fe340a18998e18ffdcfa81b": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HBoxModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HBoxModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HBoxView",
            "box_style": "",
            "children": [
              "IPY_MODEL_894a6eccd0b24c46a49541086d5de8a9",
              "IPY_MODEL_0fff971ee8544a61b696c553effb86e2",
              "IPY_MODEL_6efe779368a844deb3b1c6b01d69e10b"
            ],
            "layout": "IPY_MODEL_85e47a7d0421413fbad4394eea4ecfa3"
          }
        },
        "894a6eccd0b24c46a49541086d5de8a9": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_8084f217ab8b42c89374837fdcd902cd",
            "placeholder": "​",
            "style": "IPY_MODEL_c25895a5f4604a20b0202f0af8ab85a4",
            "value": " 75%"
          }
        },
        "0fff971ee8544a61b696c553effb86e2": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "FloatProgressModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "FloatProgressModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "ProgressView",
            "bar_style": "danger",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_1cfc693b7be34683a814fe96514d2433",
            "max": 360,
            "min": 0,
            "orientation": "horizontal",
            "style": "IPY_MODEL_877102ec558046e083a8d53aea385a88",
            "value": 270
          }
        },
        "6efe779368a844deb3b1c6b01d69e10b": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_8cca009150a444ac934a247a6a167fdb",
            "placeholder": "​",
            "style": "IPY_MODEL_7eb9cee1a80e460a9717a829b88d9065",
            "value": " 270/360 [07:31&lt;00:55,  1.63it/s, epoch=14]"
          }
        },
        "85e47a7d0421413fbad4394eea4ecfa3": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "8084f217ab8b42c89374837fdcd902cd": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "c25895a5f4604a20b0202f0af8ab85a4": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        },
        "1cfc693b7be34683a814fe96514d2433": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "877102ec558046e083a8d53aea385a88": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "ProgressStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "ProgressStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "bar_color": null,
            "description_width": ""
          }
        },
        "8cca009150a444ac934a247a6a167fdb": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "7eb9cee1a80e460a9717a829b88d9065": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        }
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}